import { mix, clone, isString } from '@antv/util';

class Command {
    constructor() {
        this._cfgs = this.getDefaultCfg();
        this.list = []; // 注册的命令集合
        this.queue = []; // 用户撤销重做
    }

    getDefaultCfg() {
        return { _command: { zoomDelta: 0.1, queue: [], current: 0, clipboard: [] } };
    }

    get(key) {
        return this._cfgs[key];
    }
    set(key, val) {
        this._cfgs[key] = val;
    }
    // 初始化插件
    initPlugin(graph) {
        this.initCommands();
        // 获取命令队列
        graph.getCommands = () => {
            return this.get('_command').queue;
        };
        // 获取当前命令
        graph.getCurrentCommand = () => {
            const c = this.get('_command');
            return c.queue[c.current - 1];
        };
        // 执行命令
        graph.executeCommand = (name, cfg) => {
            this.execute(name, graph, cfg);
        };
        // 命令是否可以被执行
        graph.commandEnable = name => {
            return this.enable(name, graph);
        };
    }
    // 注册命令
    registerCommand(name, cfg) {
        if (this[name]) {
            mix(this[name], cfg);
        } else {
            const cmd = mix(
                {},
                {
                    name: name, // add、update、autoFit、redo、undo
                    shortcutCodes: [], // 键盘命令
                    queue: true, // 是否入队，支持撤销重做
                    executeTimes: 1, // 撤销次数
                    init() { // 初始化调用函数
                        console.log('init');
                    },
                    enable() { // 是否可操作
                        return true;
                    },
                    execute(graph) { // 命令执行
                        this.snapShot = graph.save(); // 获取图数据
                        this.selectedItems = graph.get('selectedItems'); // 获取当前选中的元素
                        // 执行命令
                        if (this.method) {
                            if (isString(this.method)) {
                                graph[this.method]();
                            } else {
                                this.method(graph);
                            }
                        }
                    },
                    back(graph) { // 命令撤销
                        graph.read(this.snapShot);
                        graph.set('selectedItems', this.selectedItems);
                    }
                },
                cfg
            );
            // 将命令挂载到Command实例上
            this[name] = cmd;
            // 将命令push到list中
            this.list.push(cmd);
        }
    }
    // 执行命令
    execute(name, graph, cfg) {
        const cmd = mix({}, this[name], cfg);
        const manager = this.get('_command');
        // 如果可以点击操作
        if (cmd.enable(graph)) {
            // 调用初始化函数
            cmd.init();
            if (cmd.queue) {
                manager.queue.splice(manager.current, manager.queue.length - manager.current, cmd);
                manager.current++;
            }
        }
        // 执行命令
        graph.emit('beforecommandexecute', { command: cmd });
        cmd.execute(graph);
        graph.emit('aftercommandexecute', { command: cmd });
        return cmd;
    }
    // 是否可点击
    enable(name, graph) {
        return this[name]?.enable(graph);
    }
    // 销毁插件
    destroyPlugin() {
        this._events = null;
        this._cfgs = null;
        this.list = [];
        this.queue = [];
        this.destroyed = true;
    }
    // 删除子流程节点
    _deleteSubProcessNode(graph, itemId) {
        const subProcess = graph.find('node', node => {
            if (node.get('model')) {
                const clazz = node.get('model').clazz;
                if (clazz === 'subProcess') {
                    const containerGroup = node.getContainer();
                    const subGroup = containerGroup.subGroup;
                    const item = subGroup.findById(itemId);
                    return subGroup.contain(item);
                } else {
                    return false;
                }
            } else {
                return false;
            }
        });
        if (subProcess) {
            const group = subProcess.getContainer();
            const resultModel = group.removeItem(subProcess, itemId);
            graph.updateItem(subProcess, resultModel);
        }
    }
    // 初始化命令-注册命令
    initCommands() {
        const cmdPlugin = this;
        // 删除
        cmdPlugin.registerCommand('delete', {
            enable: function (graph) {
                const mode = graph.getCurrentMode();
                const selectedItems = graph.get('selectedItems');
                return mode === 'edit' && selectedItems && selectedItems.length > 0;
            },
            method: function (graph) {
                const selectedItems = graph.get('selectedItems');
                graph.emit('beforedelete', { items: selectedItems });
                if (selectedItems && selectedItems.length > 0) {
                    selectedItems.forEach(i => {
                        const node = graph.findById(i);
                        if (node) {
                            graph.remove(i);
                        } else {
                            cmdPlugin._deleteSubProcessNode(graph, i);
                        }
                    });
                }
                graph.emit('afterdelete', { items: selectedItems });
            },
            shortcutCodes: ['Delete', 'Backspace']
        });
        // 重做
        cmdPlugin.registerCommand('redo', {
            queue: false,
            enable: function (graph) {
                const mode = graph.getCurrentMode();
                const manager = cmdPlugin.get('_command');
                return mode === 'edit' && manager.current < manager.queue.length;
            },
            execute: function (graph) {
                const manager = cmdPlugin.get('_command');
                const cmd = manager.queue[manager.current];
                if (cmd) {
                    cmd.execute(graph);
                }
                manager.current++;
            },
            shortcutCodes: [
                ['metaKey', 'shiftKey', 'z'],
                ['ctrlKey', 'shiftKey', 'z']
            ]
        });
        // 撤销
        cmdPlugin.registerCommand('undo', {
            queue: false,
            enable: function (graph) {
                const mode = graph.getCurrentMode();
                return mode === 'edit' && cmdPlugin.get('_command').current > 0;
            },
            execute: function (graph) {
                const manager = cmdPlugin.get('_command');
                const cmd = manager.queue[manager.current - 1];
                if (cmd) {
                    cmd.executeTimes++;
                    cmd.back(graph);
                }
                manager.current--;
            },
            shortcutCodes: [
                ['metaKey', 'z'],
                ['ctrlKey', 'z']
            ]
        });
        // 拷贝
        cmdPlugin.registerCommand('copy', {
            queue: false,
            enable: function (graph) {
                const mode = graph.getCurrentMode();
                const items = graph.get('selectedItems');
                return mode === 'edit' && items && items.length > 0;
            },
            method: function (graph) {
                const manager = cmdPlugin.get('_command');
                manager.clipboard = [];
                const items = graph.get('selectedItems');
                if (items && items.length > 0) {
                    const item = graph.findById(items[0]);
                    if (item) {
                        manager.clipboard.push({ type: item.get('type'), model: item.getModel() });
                    }
                }
            }
        });
        // 粘贴
        cmdPlugin.registerCommand('paste', {
            enable: function (graph) {
                const mode = graph.getCurrentMode();
                return mode === 'edit' && cmdPlugin.get('_command').clipboard.length > 0;
            },
            method: function (graph) {
                const manager = cmdPlugin.get('_command');
                this.pasteData = clone(manager.clipboard[0]);
                const addModel = this.pasteData.model;
                if (addModel.x) {
                    addModel.x += 10;
                }
                if (addModel.y) {
                    addModel.y += 10;
                }
                const { clazz = 'userTask' } = addModel;
                const timestamp = new Date().getTime();
                const id = clazz + timestamp;
                addModel.id = id;
                const item = graph.add(this.pasteData.type, addModel);
                item.toFront();
            }
        });
        // 放大
        cmdPlugin.registerCommand('zoomIn', {
            queue: false,
            enable: function (graph) {
                const zoom = graph.getZoom();
                const maxZoom = graph.get('maxZoom');
                const minZoom = graph.get('minZoom');
                return zoom <= maxZoom && zoom >= minZoom;
            },
            execute: function (graph) {
                const manager = cmdPlugin.get('_command');
                const maxZoom = graph.get('maxZoom');
                const zoom = graph.getZoom();
                this.originZoom = zoom;
                let currentZoom = zoom + manager.zoomDelta;
                if (currentZoom > maxZoom) currentZoom = maxZoom;
                graph.zoomTo(currentZoom);
            },
            back: function (graph) {
                graph.zoomTo(this.originZoom);
            },
            shortcutCodes: [
                ['metaKey', '='],
                ['ctrlKey', '=']
            ]
        });
        // 缩小
        cmdPlugin.registerCommand('zoomOut', {
            queue: false,
            enable: function (graph) {
                const zoom = graph.getZoom();
                const maxZoom = graph.get('maxZoom');
                const minZoom = graph.get('minZoom');
                return zoom <= maxZoom && zoom >= minZoom;
            },
            execute: function (graph) {
                const manager = cmdPlugin.get('_command');
                const minZoom = graph.get('minZoom');
                const zoom = graph.getZoom();
                this.originZoom = zoom;
                let currentZoom = zoom - manager.zoomDelta;
                if (currentZoom < minZoom) currentZoom = minZoom;
                graph.zoomTo(currentZoom);
            },
            back: function (graph) {
                graph.zoomTo(this.originZoom);
            },
            shortcutCodes: [
                ['metaKey', '-'],
                ['ctrlKey', '-']
            ]
        });
        // 重置缩放（自适应）
        cmdPlugin.registerCommand('resetZoom', {
            queue: false,
            execute: function (graph) {
                const zoom = graph.getZoom();
                this.originZoom = zoom;
                graph.zoomTo(1);
            },
            back: function (graph) {
                graph.zoomTo(this.originZoom);
            }
        });
        // 1:1
        cmdPlugin.registerCommand('autoFit', {
            queue: false,
            execute: function (graph) {
                const zoom = graph.getZoom();
                this.originZoom = zoom;
                graph.fitView = true;
            },
            back: function (graph) {
                graph.zoomTo(this.originZoom);
            }
        });
        // 置顶
        cmdPlugin.registerCommand('toFront', {
            queue: false,
            enable: function (graph) {
                const items = graph.get('selectedItems');
                return items && items.length > 0;
            },
            execute: function (graph) {
                const items = graph.get('selectedItems');
                if (items && items.length > 0) {
                    const item = graph.findById(items[0]);
                    item.toFront();
                    graph.paint();
                }
            },
            back: function (graph) {
                console.log(graph);
            }
        });
        // 返回
        cmdPlugin.registerCommand('toBack', {
            queue: false,
            enable: function (graph) {
                const items = graph.get('selectedItems');
                return items && items.length > 0;
            },
            execute: function (graph) {
                const items = graph.get('selectedItems');
                if (items && items.length > 0) {
                    const item = graph.findById(items[0]);
                    item.toBack();
                    graph.paint();
                }
            },
            back: function (graph) {
                console.log(graph);
            }
        });
    }
}
export default Command;
